/*
 * Copyright (C) 2012 by Hao Hou<ghost89413@gmail.com>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>*
 */
#include <xim.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <init.h>
#include <stdio.h>
#include <protocol.h>
#define XK_MISCELLANY
#define XK_LATIN1
#include <X11/keysymdef.h>
#include <event.h>
Window xim_window;
char xim_locale[256];
XIMS xims;
static XIMEncoding zhEncodings[] = {
    "COMPOUND_TEXT",
    NULL
};
static XIMStyle Styles[] = {
    XIMPreeditPosition | XIMStatusArea, //OverTheSpot
    XIMPreeditPosition | XIMStatusNothing,      //OverTheSpot
    XIMPreeditPosition | XIMStatusNone, //OverTheSpot
    XIMPreeditNothing | XIMStatusNothing,       //Root
    XIMPreeditNothing | XIMStatusNone,  //Root
    0
};
int __xim_c_prefix__(const char *p, const char * q)
{
	int ret = 0;
	for(; *p && *q && *p == *q; p++, q++) ret++;
	if(*q == 0) return ret;
	return 0;
}
#define NumOf(a) (sizeof(a)/sizeof(*a))
void SetTriggerKey(XIMTriggerKey *key, TriggerKey* vkey,const char* desc)
{
	static const char const * keystr[] = {"CTRL", "SHIFT", "ALT","  SUPER","LCTRL",	//0-4
					      "RCTRL","LSHIFT","RSHIFT","RALT","LALT",	//5-9
					      "LSUPER","RSUPER","SPACE"};		//10-12
	static const int mask[NumOf(keystr)] = {ControlMask, ShiftMask, Mod1Mask,Mod4Mask};
	static const int vmask[NumOf(keystr)]={
		STATE_CTRL,STATE_SHIFT,STATE_ALT,STATE_SUPER,STATE_CTRL,
		STATE_CTRL,STATE_SHIFT,STATE_SHIFT,STATE_ALT,STATE_ALT,
		STATE_SUPER,STATE_SUPER,0
	};
	static const int syms[] = {0,0,0,0,XK_Control_L,
			 	   XK_Control_R,XK_Shift_L,XK_Shift_R,XK_Alt_R,XK_Alt_L,
				   XK_Super_L,XK_Shift_R,XK_space};
	int sp = 0;
	while(desc[sp])
	{
		int code,len;
		for(code = 0; code < 13; code ++)
			if((len = __xim_c_prefix__(desc + sp,keystr[code]))) break;
		int cur_mask, cur_vmask, cur_sym;
		if(len == 0)
		{
			if(desc[sp] && desc[sp] == '_')
			{
				cur_mask = 0;
				cur_vmask = 0;
				cur_sym = desc[sp];
				if(cur_sym >= 'A' && cur_sym <= 'Z')
					cur_sym += 32;
			}
		}
		else
		{
			cur_mask = mask[code];
			cur_vmask = vmask[code];
			cur_sym = syms[code];
		}
		if(len == 0)
		{	
			VINE_LOG_WARNING("Ignored invalid key code %s",desc);
			break;
		}
		sp += len;
		while(desc[sp] == '_') sp ++;
		if(key) key->modifier |= cur_mask;
		if(key) key->modifier_mask |= cur_mask;
		if(vkey) vkey->state |= cur_vmask;
		if(syms[code]) 
		{
			if(key) key->keysym = cur_sym;
			if(vkey)vkey->sym = cur_sym;
		}
	}
}
int LoadKeyGroup(const char* kg_name, TriggerKeys* tks, XIMTriggerKeys* xtks)
{
	int *tmp, tmp_v = 0,i;
	int count = *(int*)FetchValueEx(config,Integer,&tmp,&tmp_v,"%s.Count",kg_name);
	if(xtks) 
	{
		xtks->count_keys = count;
		xtks->keylist=(XIMTriggerKey*)malloc(sizeof(XIMTriggerKey)*(count+1));
		memset(xtks->keylist, 0, sizeof(XIMTriggerKey) * (count + 1));
	}
	if(tks) 
	{
		tks->count = count;
		tks->keys = (TriggerKey*)malloc(sizeof(TriggerKey)* count);
		memset(tks->keys,0,sizeof(TriggerKey) * count);
	}
	for(i = 0; i < count; i ++)
	{
		char* keystr;
		FetchValueEx(config,String,&keystr,"CTRL_SPACE","%s[%d]",kg_name,i);
		TriggerKey* p_tk = NULL;
		XIMTriggerKey* p_xtk = NULL;
		if(xtks) p_xtk = xtks->keylist + i;
		if(tks)  p_tk  = tks->keys + i;
		SetTriggerKey(p_xtk, p_tk, keystr);
		if(p_tk) VINE_LOG_DEBUG("Keycode parsed : %s(sym=0x%x state=0x%x)",keystr,
									  p_tk->sym,
									  p_tk->state);
	}
	return 0;
}
int InitXIM(Display* display)
{
	const char* imname;
	FetchValue(config,String,&imname,NULL,"IMName");
	xim_window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0,0,1,1,1,0,0);
    if(0 == xim_window)
	{
		VINE_LOG_ERROR("Can not create xim_window");
		return -1;
	}
	if(!imname)
	{
		imname = (const char*)getenv("XMODIFIERS");
		if(imname)
		{
			if(NULL != strstr(imname,"@im="))
				imname +=4;
			else
				imname = DEFAULT_IMNAME;
		}
		else
		       imname = DEFAULT_IMNAME;

	}
	VINE_LOG_DEBUG("@im=%s",imname);
	const char* lc = NULL;
	if((lc = getenv("LC_CTYPE")) ||
	   (lc = getenv("LC_ALL"))   ||
	   (lc = getenv("LANG")))
		snprintf(xim_locale,256,"%s,%s",DEFAULT_LOCALE,lc);
	else
		snprintf(xim_locale,256,"%s",DEFAULT_LOCALE);
	VINE_LOG_DEBUG("Locale = %s",xim_locale);	
	XIMTriggerKeys* onkeys = (XIMTriggerKeys*)malloc(sizeof(XIMTriggerKeys));
	LoadKeyGroup("TriggerKeys", &trigger_keys, onkeys);
	LoadKeyGroup("SwitchKeys", &switch_keys, NULL);

	XIMEncodings* encoding = (XIMEncodings*)malloc(sizeof(XIMEncodings));
	encoding->count_encodings = NumOf(zhEncodings) - 1;
	encoding->supported_encodings = zhEncodings;
	
	XIMStyles* styles = (XIMStyles*)malloc(sizeof(XIMStyles));
	styles->count_styles = NumOf(Styles) - 1;
	styles->supported_styles = Styles;
	
    xims = IMOpenIM(display,
			IMModifiers, "Xi18n",
			IMServerWindow, xim_window,
			IMServerName, imname,
			IMLocale, xim_locale,
			IMServerTransport, "X/",
			IMEncodingList, encoding,
			IMInputStyles, styles,
			IMFilterEventMask, KeyPressMask|KeyReleaseMask,
			IMOnKeysList, onkeys,
			IMProtocolHandler, VineProtocolHandler,
			NULL);
	free(styles);
	free(encoding);
	free(onkeys);
	if(NULL == xims)
	{
		VINE_LOG_FATAL("Can not Open IM");
		return -1;
	}
	VINE_LOG_TRACE("XIM Initialized successfully");	
	return 0;
}
void FinalizeXIM()
{
	if(xims) IMCloseIM(xims);
}
